package net.slteam.base.support;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import net.slteam.base.ISLEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;

/**
 * ClassName: SLWebAppConfigurer
 * Description: 该配置用于支持Http传递ISLEnum类型的参数
 * date: 2019-10-10 09:01
 *
 * @author calm
 * @since JDK 1.8
 */
public class SLEnumWebConfigurer implements WebMvcConfigurer {

    public SLEnumWebConfigurer(MappingJackson2HttpMessageConverter converter) {
        converter.getObjectMapper().registerModule(new SLEnumModule());
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new SLStringToEnumConverterFactory());
    }

    /**
     * ClassName: SLEnumWebConfigurer
     * Description: Jackson 反序列化支持
     * date: 2023/8/14 10:42 PM
     *
     * @author calm
     * @since JDK 1.8
     */
    public static class SLEnumDeserializers extends SimpleDeserializers {

        @Override
        public JsonDeserializer<?> findEnumDeserializer(Class<?> type,
                                                        DeserializationConfig config,
                                                        BeanDescription beanDesc) throws
                JsonMappingException {
            if (!type.isEnum() || !type.isAssignableFrom(ISLEnum.class)) {
                return null;
            }
            return new SLEnumJsonDeserializer(type);
        }

        public static class SLEnumJsonDeserializer<T extends Enum<T> & ISLEnum> extends JsonDeserializer<T> {

            private final Class<T> clz;

            private final T[] clzEnums;

            public SLEnumJsonDeserializer(Class<T> t) {
                this.clz = t;
                this.clzEnums = t.getEnumConstants();
            }

            @Override
            public T deserialize(JsonParser p,
                                 DeserializationContext ctxt) throws IOException {
                int value = p.getValueAsInt();
                for (T clzEnum : clzEnums) {
                    if (value == clzEnum.getCode()) {
                        return clzEnum;
                    }
                }
                return null;
            }
        }
    }

    public static class SLEnumModule extends Module {

        @Override
        public String getModuleName() {
            return "SLEnumModule";
        }

        @Override
        public Version version() {
            return Version.unknownVersion();
        }

        @Override
        public void setupModule(SetupContext context) {
            context.addDeserializers(new SLEnumDeserializers());
        }
    }

    /**
     * ClassName: SLEnumWebConfigurer
     * Description: 对Web传递ISLENUM类型参数的支持，仅支持顶级参数，不支持对象里的ISLENUM类型
     * date: 2023/8/14 10:07 PM
     *
     * @author calm
     * @since JDK 1.8
     */
    public final class SLStringToEnumConverterFactory implements ConverterFactory<String, ISLEnum> {

        public SLStringToEnumConverterFactory() {
        }

        @Override
        @SuppressWarnings("unchecked")
        public <T extends ISLEnum> Converter<String, T> getConverter(Class<T> targetType) {
            return new StringToSLEnum(targetType);
        }

        private class StringToSLEnum<T extends Enum<T> & ISLEnum> implements Converter<String, T> {

            private final Class<T> enumType;

            StringToSLEnum(Class<T> enumType) {
                this.enumType = enumType;
            }

            @Override
            public T convert(String source) {

                boolean isNumeric = StringUtils.isNumeric(source);
                for (T enumConstant : enumType.getEnumConstants()) {
                    if (isNumeric) {
                        if (enumConstant.getCode().equals(Integer.valueOf(source))) {
                            return enumConstant;
                        }
                    } else {
                        if (enumConstant.getDescription().equals(source)) {
                            return enumConstant;
                        }
                    }
                }

                return null;
            }
        }
    }
}
